#include "Avalon/WebServices/HttpSessionHandler.hpp"
#include "Avalon/Base/Algorithm.hpp"
#include "Avalon/ServiceLocator/ServiceLocator.hpp"
#include "Avalon/WebServices/HttpServerRequest.hpp"
#include "Avalon/WebServices/HttpServerResponse.hpp"
#include "Avalon/WebServices/HttpSession.hpp"
#include "Avalon/WebServices/HttpSessionFactory.hpp"

using namespace Avalon;
using namespace Avalon::WebServices;
using namespace boost;
using namespace boost::posix_time;
using namespace boost::signals2;
using namespace std;

const time_duration HttpSessionHandler::TIMEOUT = minutes(15);
const string HttpSessionHandler::SESSION_ID = "sessionid";

HttpSessionHandler::HttpSessionHandler(HttpSessionFactory* sessionFactory,
    time_duration timeout)
    : m_sessionFactory(sessionFactory) {}

HttpSessionHandler::~HttpSessionHandler() {
  Clear(m_sessions);
}

connection HttpSessionHandler::ConnectNewSessionSignal(
    const NewSessionSignal::slot_type& slot) {
  boost::lock_guard<boost::mutex> lock(m_mutex);
  return m_newSessionSignal.connect(slot);
}

connection HttpSessionHandler::ConnectSessionExpiredSignal(
    const SessionExpiredSignal::slot_type& slot) {
  boost::lock_guard<boost::mutex> lock(m_mutex);
  return m_sessionExpiredSignal.connect(slot);
}

HttpServer::HttpRequestSignal::slot_type HttpSessionHandler::GetSlot(
    const HttpRequestSignal::slot_type& slot) {
  boost::lock_guard<boost::mutex> lock(m_mutex);
  return bind(&HttpSessionHandler::OnRequest, this, _1, _2, slot);
}

HttpSession* HttpSessionHandler::CreateSession() {
  HttpSession* session = m_sessionFactory->Create();
  string sessionId;
  do {
    sessionId = ServiceLocator::GenerateSessionId();
  } while(m_sessions.find(sessionId) != m_sessions.end());
  Cookie sessionCookie(SESSION_ID, sessionId);
  session->SetSessionCookie(sessionCookie);
  m_sessions.insert(make_pair(sessionId, session));
  m_newSessionSignal(session);
  return session;
}

void HttpSessionHandler::OnRequest(HttpServerRequest* request,
    HttpServerResponse* response, const HttpRequestSignal::slot_type& slot) {
  boost::lock_guard<boost::mutex> lock(m_mutex);
  const Cookie& sessionCookie = request->GetCookie(SESSION_ID);
  HttpSession* session;
  if(sessionCookie.GetName().empty()) {
    session = CreateSession();
  } else {
    map<string, HttpSession*>::const_iterator sessionIterator =
      m_sessions.find(sessionCookie.GetValue());
    if(sessionIterator == m_sessions.end()) {
      response->SetStatus(HttpStatusCode::REQUEST_TIMEOUT);
      response->SendResponse();
      return;
    } else {
      session = sessionIterator->second;
    }
  }
  response->AddCookie(session->GetSessionCookie());
  slot(session, request, response);
}
